Expression Language

The expression language in JADE is used to specify computations right in configuration. Many configuration elements in JADE, especially in plugin configurations, support the use of expressions. In its simplest form, the expression language looks just like “written equations” such as:

2 * 3 + 5

which would of course evaluate to 11. Standard arithmetic operators (ex. * for multiplication) and boolean operators (ex. == for equality checking) may be used and will respect the standard order of precendence. Furthermore, many functions are provided for you (ex. SIN); a complete List Of Functions is also provided. Parentheses can also be used to dictate the order in which expressions are evaluated such as:

3 - (5 - 4)

which would of course evaluate to 2. One thing to keep in mind is that all functions expect valid JSON types to be passed. For example, when passing a string it must be quote-wrapped (ex. "hello kitty").

Variable

Expressions become powerful when used to transform data received from a device or another plugin. For example, you may wish to transform data received from some publisher before updating a chart. In this case, we need some kind of “variable syntax” to use in our expressions. Variables in JADE often take the form

@VAR{variable_name}

Sometimes VAR will be replaced with other “variable container names” (think of these a bit like namespaces) such as PUB for data received from some publisher, but the general form of variables is always:

@CONTAINER{variable_name}

Let’s take a look at an example of a variable used in an expression. For the moment we won’t assign the result to anything - for now we’re just focused on the variable syntax used in expressions. Suppose a plugin receives a temperature value in Celcius which is made available to parts of the plugin configuration as

@VAR{temperature}

Well, let’s write an expression to covert it to Fahrenheit:

(9/5)*@VAR{temperature}+32

We could use such an expression, for example, to set the value of a new data point for a plot in a chart or graph. Notably, whitespace is generally optional. We could equivalently write that expression (perhaps for readability) as

(9/5) * @VAR{temperature} + 32

Below we’ll see that in order to tell JADE to interpret the result of our expression as a number (vs a string), we’ll need to add some return type syntax.

Return Types

When we use expressions, they will inevitably be assigned to some configuration parameter. The expression language in JADE does not attempt to auto-detect return types. Rather, we must explicitly indicate the return type to use, otherwise it will return the value as type string.

Let’s look at an example of the “return type” syntax in the context of a snippet of an XY Chart configuration. To start, let’s explain a bit about how the XY Chart plugin works. Generally, the XY Chart plots data vs time and allows us to subscribe to data from any publishing plugins. When published data arrives, it is put into a buffer and is used to update the chart at the specified update rate. But the XY Chart also allows us to use expressions to transform the incoming data before we store it.

Below is a snippet of an XY Chart configuration corresponding to a plot definition, where notice the data element allows us to define the timestamp, value, and unit of the data (where below we apparently receive data for timestamp and temperature from the Temperature Publisher, and the XY Chart has put that data into a variable container named VAR for us). More specifically, take note of the syntax which has the form: Float:( ... ).

{
    "name": "Temperature",
    "subscription": "Temperature Publisher",
    "data": {
        "timestamp": "Float:(@VAR{timestamp})",
        "value": "Float:( (9/5) * @VAR{temperature} + 32 )",  // <-- the line of interest here
        "unit": "Fahrenheit"
    },
    "min": 0,
    "max": 10,
    "historyLength": 100,
    "properties": {
        "visible": true,
        "antiAlias": false,
        "pointStyle": "circleFull",
        "lineStyle": "solid",
        "lineWidth": "1",
        "fillTo": "none",
        "interpolation": "linear"
    }
}

The Float:( ... ) syntax instructs JADE to return the value as a floating point value (i.e. a double-precision floating point value). This is required by the XY Chart because it expects to receive the timestamp and value elements as numbers. Notice that we see a familiar expression in the value element - it seems to be converting a received @VAR{temperature} in Celcius to Fahrenheit. And the entire expression sits inside the Float:( ... ) return type wrapping.

Also notice that the whole expression sits in double quotes: expressions are always specified in the form of a string in the JSON configuration. JADE evalutates expressions in strings wherever expressions are supported and uses the return type syntax to determine which type to return (i.e. which type is used to update the corresponding configuration option). If no return type is specified, the expression will still be computed but the value will remain a string instead of being stored as a number.

The available return types are:

  • String
  • Boolean
  • Float
  • Integer
  • Array
  • Object

Functions

As noted above, several functions are provided in addition to the core arithmetic operations such as + (addition), - (subtraction), * (multiplcation), / (division), ^ (power), etc. and parameters passed to functions must be a valid JSON type. Many functions are provided and the syntax is natural. For exmaple, to compute the sine of 0 you can use SIN(0). See the List Of Functions for a list of functions with descriptions and exmaples.

Note on JSON Special Characters

Functions shake out cleaning in many cases. However because our expressions always live in a JSON string inside some configuration, anytime we have JSON special characters in our expresssions they’ll need to be “escaped” (i.e. “slashed”). If you aren’t already familiar with JSON and the notion of special characters requiring “escaping” or “slashing”, check out our JSON Overview. Either way, let’s take a look at an example where a function requires a string parameter, which will require us to specify a " character inside our JSON string (which must then be “slashed”).

JADE provides a GetDateTime function which expects a format argument of type string, such as: GetDateTime("SecondsSinceEpoch"). Now, that looks reasonable because we’ve specified a string input to the function as "SecondsSinceEpoch" with quotes as required. However, let’s see what happens when we want to use this in a configuration context, which is itself inherently JSON. We’ll just use some dummy configuration snippet below to unveil the nuance here:

// THIS IS INCORRECT :(

{
    "timestamp": "Float:( GetDateTime("SecondsSinceEpoch") )"
}

The above would be INCORRECT and you’d see an error in the configuration editor. This is becuase the " around our SecondsSinceEpoch will be interpreted as string delimiters instead of being used as characeters within the string which holds our full expression. Below is what we need to do to properly specify " characters in such cases:

// THIS IS CORRECT :)
{
    "timestamp": "Float:( GetDateTime(\"SecondsSinceEpoch\") )"
}

Notice we must use \" instead of simply " around the original SecondsSinceEpoch bit. That is, we have now properly “slashed” the special characeters so our configuration knows we meant to use a literal quote character as part of our expression. Again, if this doesn’t make sense immediately, spend a minute with our JSON Overview for more details.

Return Types (Revisited)

We also allow putting return types right in front of function names, though this only has meaning for a function used as the outermost operation in our expression. For example, our timestamp element above could be rewritten as:

{
    "timestamp": "Float:GetDateTime(\"SecondsSinceEpoch\")"
}

What’s really happening here is that when we use the Float:( ... ) syntax, the outer most function is just the unnamed function which instructs JADE to simply “evaluate the innards”. It’s like using parentheses anywhere else in your expressions; the innards are simply evaluated. If we provide a named function, JADE will simply use that function and take whatever is inside as it’s argument (or arguments if comma-delimited). This form is never required but can be slighly more performant since it removes one layer of function evaluation. It’s also arguably a bit easier on the eyes since it has less syntax (i.e. fewer characters).

Ignoring Variables

Sometimes it can be useful to ignore variables in a string. For exmaple, perhaps for some reason we want to actually send a message which literally contains a string like "... @VAR{temperature} ..." even if @VAR{temperature} is an available variable in that context. In that case, we would like a way to tell JADE to ignore replacing variables. One way to achieve this is to wrap our string in ``` characters such as:

{
    "value": "`We can access the temperature using syntax like: @VAR{temperature}.`"
}

In that exmaple above, the string assigned to value would be: We can access the temperature using syntax like: @VAR{temperature}. even if @VAR{temperature} existed as a variable in that context. The other way to ignore variables (on a per variable basis) is to use variable “flags” which we discuss in its own section below.

Ignoring Expressions

Sometimes it can be useful to ignore entire expressions. In our case, this will mean also ignore variables for nuanced reasons which we will not dive into here. For now, let’s suppose we want to actually send a message which literally contains a string like "... SIN(0) ..." instead of evalutating SIN(0) as the SIN function with parameter 2 (as an aside, note that the SIN function takes an argument in units of radians). In that case, we would like a way to tell JADE to ignore the expression. There are a couple of ways to do this, but let’s focus on the case where we want to ignore variables in the whole string we’ve specified. To achieve this, can wrap our string in ~ characters such as:

{
    "value": "~We can use functions with syntax like: SIN(0).~"
}

In that exmaple above, the string assinged to value would be: We can use functions with syntax like: SIN(0). where SIN(0) would not be evaluated.

Variable Flags

Variable flags allow us to instruct JADE as to how to interpret and process variables. Let’s take a look at an example of a variable which uses the ignore flag to see variable flags in action:

@(?i)VAR{temperature}

Notice the inclusion of the (?i) after the @ symbol. Here we’ve specified the i flag to tell JADE to ignore the variable (i.e. don’t replace it with it’s value).

The following are valid variable flags:

  • r recursive (start from previous search location in string; useful when variabeles are used within variables)
  • d delete (replace the variable with empty string)
  • i ignore (don’t replace the variable)
  • c clean (removes the flags syntax from the variable syntax; only applies if the ‘i’ flag is set)
  • d disable (disables all flags so the variable behaves as if no flags were used)

In general, multiple flags can be used although some combinations will conflate. For example, if the delete flag d is used, then it doesn’t really make sense to also include the ignore flag i (after all, the variable will be removed from string when processed which sort of overrides the notion of ignoring it and leaving the syntax as-is). They are mostly self-explanatory however it’s perhaps taking a look at an example using the recursive flag r:

@VAR{@(?r)VAR{var_name}}

Let’s dissect this to really understand it. First, there’s an outer variable specified, but it’s name seems to be a variable itself: @(?r)VAR{var_name}. This is a sort of “dynamic” variable in the sense that the name of the outer variable is only determined at runtime. The inner variable must refer to some string - the name of some variable - and the outer variable will then resolve the value of the variable with that name. Importantly here, the inner variable uses the recursive flag r which ensures that JADE will not only replace the inner variable, but also replace the outer variable after resolving the inner variable. So, if @VAR{var_name} had the string value temperature, then whole crazy variable @VAR{@(?r)VAR{var_name}} would first resolve to @VAR{temperature}, and then resolve to whatever the value of temperature was in the VAR variable container. If we hadn’t specified the recursive flag r, JADE would have resolved the whole crazy variable to simply @VAR{temperature} and it would not have done the final replacement for the actual value of the temperature variable.